﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace Rabbit.Controls
{
    public class TextBoxEx : TextBox
    {
        public bool AutoAddWhiteSpaceAfterTriggered
        {
            get { return (bool)GetValue(AutoAddWhiteSpaceAfterTriggeredProperty); }
            set { SetValue(AutoAddWhiteSpaceAfterTriggeredProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AutoAddWhiteSpaceAfterTriggered.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoAddWhiteSpaceAfterTriggeredProperty =
            DependencyProperty.Register("AutoAddWhiteSpaceAfterTriggered", typeof(bool), typeof(TextBoxEx), new UIPropertyMetadata(true));

        public IList<String> ContentAssistSource
        {
            get { return (IList<String>)GetValue(ContentAssistSourceProperty); }
            set { SetValue(ContentAssistSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ContentAssistSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ContentAssistSourceProperty =
            DependencyProperty.Register("ContentAssistSource", typeof(IList<String>), typeof(TextBoxEx), new UIPropertyMetadata(new List<string>()));

        public IList<char> ContentAssistTriggers
        {
            get { return (IList<char>)GetValue(ContentAssistTriggersProperty); }
            set { SetValue(ContentAssistTriggersProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ContentAssistSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ContentAssistTriggersProperty =
            DependencyProperty.Register("ContentAssistTriggers", typeof(IList<char>), typeof(TextBoxEx), new UIPropertyMetadata(new List<char>()));

        #region constructure
        public TextBoxEx()
        {
            this.Loaded += new RoutedEventHandler(TextBoxEx_Loaded);
        }

        void TextBoxEx_Loaded(object sender, RoutedEventArgs e)
        {
            //init the assist list box
            if (this.Parent.GetType() != typeof(Canvas))
            {
                throw new Exception("this control must be put in Grid control");
            }

            Canvas canvas = this.Parent as Canvas;
            foreach (var item in canvas.Children)
            {
                if (item.GetType() == typeof(ListBox))
                {
                    this.AssistListBox = item as ListBox;
                }
            }

            if (this.AssistListBox == null)
            {
                throw new Exception("Not Find ListBox In Canvas");
            }

            if (ContentAssistTriggers.Count == 0)
            {
                ContentAssistTriggers.Add('@');
            }

            AssistListBox.MouseDoubleClick += new MouseButtonEventHandler(AssistListBox_MouseDoubleClick);
            AssistListBox.PreviewKeyDown += new KeyEventHandler(AssistListBox_PreviewKeyDown);
        }

        void AssistListBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            //if Enter\Tab\Space key is pressed, insert current selected item to richtextbox
            if (e.Key == Key.Enter || e.Key == Key.Tab || e.Key == Key.Space)
            {
                InsertAssistWord();
                e.Handled = true;
            }
            else if (e.Key == Key.Back)
            {
                //Baskspace key is pressed, set focus to richtext box
                if (sbLastWords.Length >= 1)
                {
                    sbLastWords.Remove(sbLastWords.Length - 1, 1);
                    FilterAssistBoxItemsSource();
                }
                this.Focus();
            }
        }

        void AssistListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            InsertAssistWord();
        }

        private bool InsertAssistWord()
        {
            bool isInserted = false;
            if (AssistListBox.SelectedIndex != -1)
            {
                string selectedString = AssistListBox.SelectedItem.ToString().Remove(0, sbLastWords.Length);
                if (AutoAddWhiteSpaceAfterTriggered)
                {
                    selectedString += " ";
                }
                this.InsertText(selectedString);
                isInserted = true;
            }

            AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
            sbLastWords.Clear();
            IsAssistKeyPressed = false;
            return isInserted;
        }
        #endregion

        #region Insert Text
        public void InsertText(string text)
        {
            this.Text = this.Text + text;
            this.Select(this.Text.Length, 0);
            this.Focus();
        }
        #endregion

        #region Content Assist
        private bool IsAssistKeyPressed = false;
        private System.Text.StringBuilder sbLastWords = new System.Text.StringBuilder();
        private ListBox AssistListBox = new ListBox();

        protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
        {
            ResetAssistListBoxLocation();

            if (e.Key == System.Windows.Input.Key.Back)
            {
                if (sbLastWords.Length > 0)
                {
                    sbLastWords.Remove(sbLastWords.Length - 1, 1);
                    FilterAssistBoxItemsSource();
                }
                else
                {
                    IsAssistKeyPressed = false;
                    sbLastWords.Clear();
                    AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
                }
            }

            //enter key pressed, insert the first item to richtextbox
            if ((e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Tab))
            {
                AssistListBox.SelectedIndex = 0;
                if (InsertAssistWord())
                {
                    e.Handled = true;
                }
            }

            if (e.Key == Key.Down)
            {
                AssistListBox.Focus();
            }

            base.OnPreviewKeyDown(e);
        }

        private void FilterAssistBoxItemsSource()
        {
            if (this.FilterItems != null)
            {
                this.FilterItems(this, sbLastWords.ToString());
            }

            if (AssistListBox.Items.Count == 0)
            {
                AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
            }
            else
            {
                AssistListBox.SelectedIndex = 0;
                AssistListBox.Visibility = System.Windows.Visibility.Visible;
            }
        }

        protected override void OnTextInput(System.Windows.Input.TextCompositionEventArgs e)
        {
            base.OnTextInput(e);
            if (IsAssistKeyPressed == false && e.Text.Length == 1)
            {
                if (ContentAssistTriggers.Contains(char.Parse(e.Text)))
                {
                    ResetAssistListBoxLocation();
                    IsAssistKeyPressed = true;
                    FilterAssistBoxItemsSource();
                    return;
                }
            }

            if (IsAssistKeyPressed)
            {
                sbLastWords.Append(e.Text);
                FilterAssistBoxItemsSource();
            }
        }

        private void ResetAssistListBoxLocation()
        {
            Rect rect = this.GetRectFromCharacterIndex(this.CaretIndex);
            double left = rect.X >= 20 ? rect.X : 20;
            double top = rect.Y >= 20 ? rect.Y + 20 : 20;
            left += this.Padding.Left;
            top += this.Padding.Top;
            AssistListBox.SetCurrentValue(ListBox.MarginProperty, new Thickness(left, top, 0, 0));
        }
        #endregion

        #region 自定义事件
        public delegate void FilterItemsHandler(object sender, string filterStr);

        public event FilterItemsHandler FilterItems;
        #endregion
    }
}
